home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Players / PlayWnd / playwnd.cpp next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  31.3 KB  |  1,181 lines

  1. //------------------------------------------------------------------------------
  2. // File: PlayWnd.cpp
  3. //
  4. // Desc: DirectShow sample code - a simple audio/video media file player
  5. //       application.  Pause, stop, mute, and fullscreen mode toggle can
  6. //       be performed via keyboard commands.
  7. //
  8. // Copyright (c) 1996-2001 Microsoft Corporation.  All rights reserved.
  9. //------------------------------------------------------------------------------
  10.  
  11.  
  12. #include <dshow.h>
  13. #include <commctrl.h>
  14. #include <commdlg.h>
  15. #include <stdio.h>
  16. #include <tchar.h>
  17. #include <atlbase.h>
  18.  
  19. #include "playwnd.h"
  20.  
  21. // An application can advertise the existence of its filter graph
  22. // by registering the graph with a global Running Object Table (ROT).
  23. // The GraphEdit application can detect and remotely view the running
  24. // filter graph, allowing you to 'spy' on the graph with GraphEdit.
  25. //
  26. // To enable registration in this sample, define REGISTER_FILTERGRAPH.
  27. //
  28. #define REGISTER_FILTERGRAPH
  29.  
  30. //
  31. // Global data
  32. //
  33. HWND      ghApp=0;
  34. HMENU     ghMenu=0;
  35. HINSTANCE ghInst=0;
  36. TCHAR     g_szFileName[MAX_PATH]={0};
  37. BOOL      g_bAudioOnly=FALSE, g_bFullscreen=FALSE;
  38. LONG      g_lVolume=VOLUME_FULL;
  39. DWORD     g_dwGraphRegister=0;
  40. PLAYSTATE g_psCurrent=Stopped;
  41. double    g_PlaybackRate=1.0;
  42.  
  43. // DirectShow interfaces
  44. IGraphBuilder *pGB = NULL;
  45. IMediaControl *pMC = NULL;
  46. IMediaEventEx *pME = NULL;
  47. IVideoWindow  *pVW = NULL;
  48. IBasicAudio   *pBA = NULL;
  49. IBasicVideo   *pBV = NULL;
  50. IMediaSeeking *pMS = NULL;
  51. IMediaPosition *pMP = NULL;
  52. IVideoFrameStep *pFS = NULL;
  53.  
  54.  
  55.  
  56. HRESULT PlayMovieInWindow(LPTSTR szFile)
  57. {
  58.     USES_CONVERSION;
  59.     WCHAR wFile[MAX_PATH];
  60.     HRESULT hr;
  61.  
  62.     // Clear open dialog remnants before calling RenderFile()
  63.     UpdateWindow(ghApp);
  64.  
  65.     // Convert filename to wide character string
  66.     wcscpy(wFile, T2W(szFile));
  67.  
  68.     // Get the interface for DirectShow's GraphBuilder
  69.     JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
  70.                          IID_IGraphBuilder, (void **)&pGB));
  71.  
  72.     if(SUCCEEDED(hr))
  73.     {
  74.         // Have the graph builder construct its the appropriate graph automatically
  75.         JIF(pGB->RenderFile(wFile, NULL));
  76.  
  77.         // QueryInterface for DirectShow interfaces
  78.         JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
  79.         JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
  80.         JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));
  81.         JIF(pGB->QueryInterface(IID_IMediaPosition, (void **)&pMP));
  82.  
  83.         // Query for video interfaces, which may not be relevant for audio files
  84.         JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
  85.         JIF(pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV));
  86.  
  87.         // Query for audio interfaces, which may not be relevant for video-only files
  88.         JIF(pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA));
  89.  
  90.         // Is this an audio-only file (no video component)?
  91.         CheckVisibility();
  92.  
  93.         // Have the graph signal event via window callbacks for performance
  94.         JIF(pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));
  95.  
  96.         if (!g_bAudioOnly)
  97.         {
  98.             JIF(pVW->put_Owner((OAHWND)ghApp));
  99.             JIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));
  100.  
  101.             JIF(InitVideoWindow(1, 1));
  102.             GetFrameStepInterface();
  103.         }
  104.         else
  105.         {
  106.             JIF(InitPlayerWindow());
  107.         }
  108.  
  109.         // Let's get ready to rumble!
  110.         CheckSizeMenu(ID_FILE_SIZE_NORMAL);
  111.         ShowWindow(ghApp, SW_SHOWNORMAL);
  112.         UpdateWindow(ghApp);
  113.         SetForegroundWindow(ghApp);
  114.         SetFocus(ghApp);
  115.         g_bFullscreen = FALSE;
  116.         g_PlaybackRate = 1.0;
  117.         UpdateMainTitle();
  118.  
  119. #ifdef REGISTER_FILTERGRAPH
  120.         hr = AddGraphToRot(pGB, &g_dwGraphRegister);
  121.         if (FAILED(hr))
  122.         {
  123.             Msg(TEXT("Failed to register filter graph with ROT!  hr=0x%x"), hr);
  124.             g_dwGraphRegister = 0;
  125.         }
  126. #endif
  127.  
  128.         // Run the graph to play the media file
  129.         JIF(pMC->Run());
  130.         g_psCurrent=Running;
  131.  
  132.         SetFocus(ghApp);
  133.     }
  134.  
  135.     return hr;
  136. }
  137.  
  138.  
  139. HRESULT InitVideoWindow(int nMultiplier, int nDivider)
  140. {
  141.     LONG lHeight, lWidth;
  142.     HRESULT hr = S_OK;
  143.     RECT rect;
  144.  
  145.     if (!pBV)
  146.         return S_OK;
  147.  
  148.     // Read the default video size
  149.     hr = pBV->GetVideoSize(&lWidth, &lHeight);
  150.     if (hr == E_NOINTERFACE)
  151.         return S_OK;
  152.  
  153.     EnablePlaybackMenu(TRUE);
  154.  
  155.     // Account for requests of normal, half, or double size
  156.     lWidth  = lWidth  * nMultiplier / nDivider;
  157.     lHeight = lHeight * nMultiplier / nDivider;
  158.  
  159.     SetWindowPos(ghApp, NULL, 0, 0, lWidth, lHeight,
  160.                  SWP_NOMOVE | SWP_NOOWNERZORDER);
  161.  
  162.     int nTitleHeight  = GetSystemMetrics(SM_CYCAPTION);
  163.     int nBorderWidth  = GetSystemMetrics(SM_CXBORDER);
  164.     int nBorderHeight = GetSystemMetrics(SM_CYBORDER);
  165.  
  166.     // Account for size of title bar and borders for exact match
  167.     // of window client area to default video size
  168.     SetWindowPos(ghApp, NULL, 0, 0, lWidth + 2*nBorderWidth,
  169.             lHeight + nTitleHeight + 2*nBorderHeight,
  170.             SWP_NOMOVE | SWP_NOOWNERZORDER);
  171.  
  172.     GetClientRect(ghApp, &rect);
  173.     JIF(pVW->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom));
  174.  
  175.     return hr;
  176. }
  177.  
  178.  
  179. HRESULT InitPlayerWindow(void)
  180. {
  181.     // Reset to a default size for audio and after closing a clip
  182.     SetWindowPos(ghApp, NULL, 0, 0,
  183.                  DEFAULT_AUDIO_WIDTH,
  184.                  DEFAULT_AUDIO_HEIGHT,
  185.                  SWP_NOMOVE | SWP_NOOWNERZORDER);
  186.  
  187.     // Check the 'full size' menu item
  188.     CheckSizeMenu(ID_FILE_SIZE_NORMAL);
  189.     EnablePlaybackMenu(FALSE);
  190.  
  191.     return S_OK;
  192. }
  193.  
  194.  
  195. void MoveVideoWindow(void)
  196. {
  197.     HRESULT hr;
  198.  
  199.     // Track the movement of the container window and resize as needed
  200.     if(pVW)
  201.     {
  202.         RECT client;
  203.  
  204.         GetClientRect(ghApp, &client);
  205.         hr = pVW->SetWindowPosition(client.left, client.top,
  206.                                     client.right, client.bottom);
  207.     }
  208. }
  209.  
  210.  
  211. void CheckVisibility(void)
  212. {
  213.     long lVisible;
  214.     HRESULT hr;
  215.  
  216.     if ((!pVW) || (!pBV))
  217.     {
  218.         // Audio-only files have no video interfaces.  This might also
  219.         // be a file whose video component uses an unknown video codec.
  220.         g_bAudioOnly = TRUE;
  221.         return;
  222.     }
  223.     else
  224.     {
  225.         // Clear the global flag
  226.         g_bAudioOnly = FALSE;
  227.     }
  228.  
  229.     hr = pVW->get_Visible(&lVisible);
  230.     if (FAILED(hr))
  231.     {
  232.         // If this is an audio-only clip, get_Visible() won't work.
  233.         //
  234.         // Also, if this video is encoded with an unsupported codec,
  235.         // we won't see any video, although the audio will work if it is
  236.         // of a supported format.
  237.         //
  238.         if (hr == E_NOINTERFACE)
  239.         {
  240.             g_bAudioOnly = TRUE;
  241.         }
  242.         else
  243.         {
  244.             Msg(TEXT("Failed(%08lx) in pVW->get_Visible()!\r\n"), hr);
  245.         }
  246.     }
  247. }
  248.  
  249.  
  250. void PauseClip(void)
  251. {
  252.     if (!pMC)
  253.         return;
  254.  
  255.     // Toggle play/pause behavior
  256.     if((g_psCurrent == Paused) || (g_psCurrent == Stopped))
  257.     {
  258.         if (SUCCEEDED(pMC->Run()))
  259.             g_psCurrent = Running;
  260.     }
  261.     else
  262.     {
  263.         if (SUCCEEDED(pMC->Pause()))
  264.             g_psCurrent = Paused;
  265.     }
  266.  
  267.     UpdateMainTitle();
  268. }
  269.  
  270.  
  271. void StopClip(void)
  272. {
  273.     HRESULT hr;
  274.  
  275.     if ((!pMC) || (!pMS))
  276.         return;
  277.  
  278.     // Stop and reset postion to beginning
  279.     if((g_psCurrent == Paused) || (g_psCurrent == Running))
  280.     {
  281.         LONGLONG pos = 0;
  282.         hr = pMC->Stop();
  283.         g_psCurrent = Stopped;
  284.  
  285.         // Seek to the beginning
  286.         hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  287.             NULL, AM_SEEKING_NoPositioning);
  288.  
  289.         // Display the first frame to indicate the reset condition
  290.         hr = pMC->Pause();
  291.     }
  292.  
  293.     UpdateMainTitle();
  294. }
  295.  
  296.  
  297. void OpenClip()
  298. {
  299.     HRESULT hr;
  300.  
  301.     // If no filename specified by command line, show file open dialog
  302.     if(g_szFileName[0] == L'\0')
  303.     {
  304.         TCHAR szFilename[MAX_PATH];
  305.  
  306.         UpdateMainTitle();
  307.  
  308.         // If no filename was specified on the command line, then our video
  309.         // window has not been created or made visible.  Make our main window
  310.         // visible and bring to the front to allow file selection.
  311.         InitPlayerWindow();
  312.         ShowWindow(ghApp, SW_SHOWNORMAL);
  313.         SetForegroundWindow(ghApp);
  314.  
  315.         if (! GetClipFileName(szFilename))
  316.         {
  317.             DWORD dwDlgErr = CommDlgExtendedError();
  318.  
  319.             // Don't show output if user cancelled the selection (no dlg error)
  320.             if (dwDlgErr)
  321.             {
  322.                 Msg(TEXT("GetClipFileName Failed! Error=0x%x\r\n"), GetLastError());
  323.             }
  324.             return;
  325.         }
  326.  
  327.         // This sample does not support playback of ASX playlists.
  328.         // Since this could be confusing to a user, display a warning
  329.         // message if an ASX file was opened.
  330.         if (_tcsstr((_tcslwr(szFilename)), TEXT(".asx")))
  331.         {
  332.             Msg(TEXT("ASX Playlists are not supported by this application.\n\n")
  333.                 TEXT("Please select a valid media file.\0"));
  334.             return;
  335.         }
  336.  
  337.         lstrcpy(g_szFileName, szFilename);
  338.     }
  339.  
  340.     // Reset status variables
  341.     g_psCurrent = Stopped;
  342.     g_lVolume = VOLUME_FULL;
  343.  
  344.     // Start playing the media file
  345.     hr = PlayMovieInWindow(g_szFileName);
  346.  
  347.     // If we couldn't play the clip, clean up
  348.     if (FAILED(hr))
  349.         CloseClip();
  350. }
  351.  
  352.  
  353. BOOL GetClipFileName(LPTSTR szName)
  354. {
  355.     static OPENFILENAME ofn={0};
  356.     static BOOL bSetInitialDir = FALSE;
  357.  
  358.     // Reset filename
  359.     *szName = 0;
  360.  
  361.     // Fill in standard structure fields
  362.     ofn.lStructSize       = sizeof(OPENFILENAME);
  363.     ofn.hwndOwner         = ghApp;
  364.     ofn.lpstrFilter       = NULL;
  365.     ofn.lpstrFilter       = FILE_FILTER_TEXT;
  366.     ofn.lpstrCustomFilter = NULL;
  367.     ofn.nFilterIndex      = 1;
  368.     ofn.lpstrFile         = szName;
  369.     ofn.nMaxFile          = MAX_PATH;
  370.     ofn.lpstrTitle        = TEXT("Open Media File...\0");
  371.     ofn.lpstrFileTitle    = NULL;
  372.     ofn.lpstrDefExt       = TEXT("*\0");
  373.     ofn.Flags             = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST;
  374.  
  375.     // Remember the path of the first selected file
  376.     if (bSetInitialDir == FALSE)
  377.     {
  378.         ofn.lpstrInitialDir = DEFAULT_MEDIA_PATH;
  379.         bSetInitialDir = TRUE;
  380.     }
  381.     else
  382.         ofn.lpstrInitialDir = NULL;
  383.  
  384.     // Create the standard file open dialog and return its result
  385.     return GetOpenFileName((LPOPENFILENAME)&ofn);
  386. }
  387.  
  388.  
  389. void CloseClip()
  390. {
  391.     HRESULT hr;
  392.  
  393.     // Stop media playback
  394.     if(pMC)
  395.         hr = pMC->Stop();
  396.  
  397.     // Clear global flags
  398.     g_psCurrent = Stopped;
  399.     g_bAudioOnly = TRUE;
  400.     g_bFullscreen = FALSE;
  401.  
  402.     // Free DirectShow interfaces
  403.     CloseInterfaces();
  404.  
  405.     // Clear file name to allow selection of new file with open dialog
  406.     g_szFileName[0] = L'\0';
  407.  
  408.     // No current media state
  409.     g_psCurrent = Init;
  410.  
  411.     // Reset the player window
  412.     RECT rect;
  413.     GetClientRect(ghApp, &rect);
  414.     InvalidateRect(ghApp, &rect, TRUE);
  415.  
  416.     UpdateMainTitle();
  417.     InitPlayerWindow();
  418. }
  419.  
  420.  
  421. void CloseInterfaces(void)
  422. {
  423.     HRESULT hr;
  424.  
  425.     // Relinquish ownership (IMPORTANT!) after hiding video window
  426.     if(pVW)
  427.     {
  428.         hr = pVW->put_Visible(OAFALSE);
  429.         hr = pVW->put_Owner(NULL);
  430.     }
  431.  
  432.     // Disable event callbacks
  433.     if (pME)
  434.         hr = pME->SetNotifyWindow((OAHWND)NULL, 0, 0);
  435.  
  436. #ifdef REGISTER_FILTERGRAPH
  437.     if (g_dwGraphRegister)
  438.     {
  439.         RemoveGraphFromRot(g_dwGraphRegister);
  440.         g_dwGraphRegister = 0;
  441.     }
  442. #endif
  443.  
  444.     // Release and zero DirectShow interfaces
  445.     SAFE_RELEASE(pME);
  446.     SAFE_RELEASE(pMS);
  447.     SAFE_RELEASE(pMP);
  448.     SAFE_RELEASE(pMC);
  449.     SAFE_RELEASE(pBA);
  450.     SAFE_RELEASE(pBV);
  451.     SAFE_RELEASE(pVW);
  452.     SAFE_RELEASE(pFS);
  453.     SAFE_RELEASE(pGB);
  454. }
  455.  
  456.  
  457. #ifdef REGISTER_FILTERGRAPH
  458.  
  459. HRESULT AddGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) 
  460. {
  461.     IMoniker * pMoniker;
  462.     IRunningObjectTable *pROT;
  463.     if (FAILED(GetRunningObjectTable(0, &pROT))) 
  464.     {
  465.         return E_FAIL;
  466.     }
  467.  
  468.     WCHAR wsz[128];
  469.     wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph, 
  470.               GetCurrentProcessId());
  471.  
  472.     HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
  473.     if (SUCCEEDED(hr)) 
  474.     {
  475.         hr = pROT->Register(0, pUnkGraph, pMoniker, pdwRegister);
  476.         pMoniker->Release();
  477.     }
  478.  
  479.     pROT->Release();
  480.     return hr;
  481. }
  482.  
  483. void RemoveGraphFromRot(DWORD pdwRegister)
  484. {
  485.     IRunningObjectTable *pROT;
  486.  
  487.     if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) 
  488.     {
  489.         pROT->Revoke(pdwRegister);
  490.         pROT->Release();
  491.     }
  492. }
  493.  
  494. #endif
  495.  
  496.  
  497. void Msg(TCHAR *szFormat, ...)
  498. {
  499.     TCHAR szBuffer[512];  // Large buffer for very long filenames (like HTTP)
  500.  
  501.     // Format the input string
  502.     va_list pArgs;
  503.     va_start(pArgs, szFormat);
  504.     _vstprintf(szBuffer, szFormat, pArgs);
  505.     va_end(pArgs);
  506.  
  507.     // Display a message box with the formatted string
  508.     MessageBox(NULL, szBuffer, TEXT("PlayWnd Sample"), MB_OK);
  509. }
  510.  
  511.  
  512. HRESULT ToggleMute(void)
  513. {
  514.     HRESULT hr=S_OK;
  515.  
  516.     if ((!pGB) || (!pBA))
  517.         return S_OK;
  518.  
  519.     // Read current volume
  520.     hr = pBA->get_Volume(&g_lVolume);
  521.     if (hr == E_NOTIMPL)
  522.     {
  523.         // Fail quietly if this is a video-only media file
  524.         return S_OK;
  525.     }
  526.     else if (FAILED(hr))
  527.     {
  528.         Msg(TEXT("Failed to read audio volume!  hr=0x%x\r\n"), hr);
  529.         return hr;
  530.     }
  531.  
  532.     // Switch volume levels
  533.     if (g_lVolume == VOLUME_FULL)
  534.         g_lVolume = VOLUME_SILENCE;
  535.     else
  536.         g_lVolume = VOLUME_FULL;
  537.  
  538.     // Set new volume
  539.     JIF(pBA->put_Volume(g_lVolume));
  540.  
  541.     UpdateMainTitle();
  542.     return hr;
  543. }
  544.  
  545.  
  546. void UpdateMainTitle(void)
  547. {
  548.     TCHAR szTitle[MAX_PATH]={0}, szFile[MAX_PATH]={0};
  549.  
  550.     // If no file is loaded, just show the application title
  551.     if (g_szFileName[0] == L'\0')
  552.     {
  553.         wsprintf(szTitle, TEXT("%s"), APPLICATIONNAME);
  554.     }
  555.  
  556.     // Otherwise, show useful information
  557.     else
  558.     {
  559.         // Get file name without full path
  560.         GetFilename(g_szFileName, szFile);
  561.  
  562.         char szPlaybackRate[16];
  563.         if (g_PlaybackRate == 1.0)
  564.             szPlaybackRate[0] = '\0';
  565.         else
  566.             sprintf(szPlaybackRate, "(Rate:%2.2f)", g_PlaybackRate);
  567.  
  568.         TCHAR szRate[20];
  569.  
  570. #ifdef UNICODE
  571.         MultiByteToWideChar(CP_ACP, 0, szPlaybackRate, -1, szRate, 20);
  572.         
  573. #else
  574.         lstrcpy(szRate, szPlaybackRate);
  575. #endif
  576.  
  577.         // Update the window title to show filename and play state
  578.         wsprintf(szTitle, TEXT("%s [%s] %s%s%s\0\0"),
  579.                 szFile,
  580.                 g_bAudioOnly ? TEXT("Audio\0") : TEXT("Video\0"),
  581.                 (g_lVolume == VOLUME_SILENCE) ? TEXT("(Muted)\0") : TEXT("\0"),
  582.                 (g_psCurrent == Paused) ? TEXT("(Paused)\0") : TEXT("\0"),
  583.                 szRate);
  584.     }
  585.  
  586.     SetWindowText(ghApp, szTitle);
  587. }
  588.  
  589.  
  590. void GetFilename(TCHAR *pszFull, TCHAR *pszFile)
  591. {
  592.     int nLength;
  593.     TCHAR szPath[MAX_PATH]={0};
  594.     BOOL bSetFilename=FALSE;
  595.  
  596.     // Strip path and return just the file's name
  597.     _tcscpy(szPath, pszFull);
  598.     nLength = (int) _tcslen(szPath);
  599.  
  600.     for (int i=nLength-1; i>=0; i--)
  601.     {
  602.         if ((szPath[i] == '\\') || (szPath[i] == '/'))
  603.         {
  604.             szPath[i] = '\0';
  605.             lstrcpy(pszFile, &szPath[i+1]);
  606.             bSetFilename = TRUE;
  607.             break;
  608.         }
  609.     }
  610.  
  611.     // If there was no path given (just a file name), then
  612.     // just copy the full path to the target path.
  613.     if (!bSetFilename)
  614.         _tcscpy(pszFile, pszFull);
  615. }
  616.  
  617.  
  618. HRESULT ToggleFullScreen(void)
  619. {
  620.     HRESULT hr=S_OK;
  621.     LONG lMode;
  622.     static HWND hDrain=0;
  623.  
  624.     // Don't bother with full-screen for audio-only files
  625.     if ((g_bAudioOnly) || (!pVW))
  626.         return S_OK;
  627.  
  628.     // Read current state
  629.     JIF(pVW->get_FullScreenMode(&lMode));
  630.  
  631.     if (lMode == OAFALSE)
  632.     {
  633.         // Save current message drain
  634.         LIF(pVW->get_MessageDrain((OAHWND *) &hDrain));
  635.  
  636.         // Set message drain to application main window
  637.         LIF(pVW->put_MessageDrain((OAHWND) ghApp));
  638.  
  639.         // Switch to full-screen mode
  640.         lMode = OATRUE;
  641.         JIF(pVW->put_FullScreenMode(lMode));
  642.         g_bFullscreen = TRUE;
  643.     }
  644.     else
  645.     {
  646.         // Switch back to windowed mode
  647.         lMode = OAFALSE;
  648.         JIF(pVW->put_FullScreenMode(lMode));
  649.  
  650.         // Undo change of message drain
  651.         LIF(pVW->put_MessageDrain((OAHWND) hDrain));
  652.  
  653.         // Reset video window
  654.         LIF(pVW->SetWindowForeground(-1));
  655.  
  656.         // Reclaim keyboard focus for player application
  657.         UpdateWindow(ghApp);
  658.         SetForegroundWindow(ghApp);
  659.         SetFocus(ghApp);
  660.         g_bFullscreen = FALSE;
  661.     }
  662.  
  663.     return hr;
  664. }
  665.  
  666.  
  667. //
  668. // Some video renderers support stepping media frame by frame with the
  669. // IVideoFrameStep interface.  See the interface documentation for more
  670. // details on frame stepping.
  671. //
  672. BOOL GetFrameStepInterface(void)
  673. {
  674.     HRESULT hr;
  675.     IVideoFrameStep *pFSTest = NULL;
  676.  
  677.     // Get the frame step interface, if supported
  678.     hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFSTest);
  679.     if (FAILED(hr))
  680.         return FALSE;
  681.  
  682.     // Check if this decoder can step
  683.     hr = pFSTest->CanStep(0L, NULL);
  684.  
  685.     if (hr == S_OK)
  686.     {
  687.         pFS = pFSTest;  // Save interface to global variable for later use
  688.         return TRUE;
  689.     }
  690.     else
  691.     {
  692.         pFSTest->Release();
  693.         return FALSE;
  694.     }
  695. }
  696.  
  697.  
  698. HRESULT StepOneFrame(void)
  699. {
  700.     HRESULT hr=S_OK;
  701.  
  702.     // If the Frame Stepping interface exists, use it to step one frame
  703.     if (pFS)
  704.     {
  705.         // The graph must be paused for frame stepping to work
  706.         if (g_psCurrent != State_Paused)
  707.             PauseClip();
  708.  
  709.         // Step the requested number of frames, if supported
  710.         hr = pFS->Step(1, NULL);
  711.     }
  712.  
  713.     return hr;
  714. }
  715.  
  716. HRESULT StepFrames(int nFramesToStep)
  717. {
  718.     HRESULT hr=S_OK;
  719.  
  720.     // If the Frame Stepping interface exists, use it to step frames
  721.     if (pFS)
  722.     {
  723.         // The renderer may not support frame stepping for more than one
  724.         // frame at a time, so check for support.  S_OK indicates that the
  725.         // renderer can step nFramesToStep successfully.
  726.         if ((hr = pFS->CanStep(nFramesToStep, NULL)) == S_OK)
  727.         {
  728.             // The graph must be paused for frame stepping to work
  729.             if (g_psCurrent != State_Paused)
  730.                 PauseClip();
  731.  
  732.             // Step the requested number of frames, if supported
  733.             hr = pFS->Step(nFramesToStep, NULL);
  734.         }
  735.     }
  736.  
  737.     return hr;
  738. }
  739.  
  740.  
  741. HRESULT ModifyRate(double dRateAdjust)
  742. {
  743.     HRESULT hr=S_OK;
  744.     double dRate;
  745.  
  746.     // If the IMediaPosition interface exists, use it to set rate
  747.     if ((pMP) && (dRateAdjust != 0))
  748.     {
  749.         if ((hr = pMP->get_Rate(&dRate)) == S_OK)
  750.         {
  751.             // Add current rate to adjustment value
  752.             double dNewRate = dRate + dRateAdjust;
  753.             hr = pMP->put_Rate(dNewRate);
  754.  
  755.             // Save global rate
  756.             if (SUCCEEDED(hr))
  757.             {
  758.                 g_PlaybackRate = dNewRate;
  759.                 UpdateMainTitle();
  760.             }
  761.         }
  762.     }
  763.  
  764.     return hr;
  765. }
  766.  
  767.  
  768. HRESULT SetRate(double dRate)
  769. {
  770.     HRESULT hr=S_OK;
  771.  
  772.     // If the IMediaPosition interface exists, use it to set rate
  773.     if (pMP)
  774.     {
  775.         hr = pMP->put_Rate(dRate);
  776.  
  777.         // Save global rate
  778.         if (SUCCEEDED(hr))
  779.         {
  780.             g_PlaybackRate = dRate;
  781.             UpdateMainTitle();
  782.         }
  783.     }
  784.  
  785.     return hr;
  786. }
  787.  
  788.  
  789. HRESULT HandleGraphEvent(void)
  790. {
  791.     LONG evCode, evParam1, evParam2;
  792.     HRESULT hr=S_OK;
  793.  
  794.     // Make sure that we don't access the media event interface
  795.     // after it has already been released.
  796.     if (!pME)
  797.         return S_OK;
  798.  
  799.     // Process all queued events
  800.     while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
  801.                     (LONG_PTR *) &evParam2, 0)))
  802.     {
  803.         // Free memory associated with callback, since we're not using it
  804.         hr = pME->FreeEventParams(evCode, evParam1, evParam2);
  805.  
  806.         // If this is the end of the clip, reset to beginning
  807.         if(EC_COMPLETE == evCode)
  808.         {
  809.             LONGLONG pos=0;
  810.  
  811.             // Reset to first frame of movie
  812.             hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  813.                                    NULL, AM_SEEKING_NoPositioning);
  814.             if (FAILED(hr))
  815.             {
  816.                 // Some custom filters (like the Windows CE MIDI filter)
  817.                 // may not implement seeking interfaces (IMediaSeeking)
  818.                 // to allow seeking to the start.  In that case, just stop
  819.                 // and restart for the same effect.  This should not be
  820.                 // necessary in most cases.
  821.                 if (FAILED(hr = pMC->Stop()))
  822.                 {
  823.                     Msg(TEXT("Failed(0x%08lx) to stop media clip!\r\n"), hr);
  824.                     break;
  825.                 }
  826.  
  827.                 if (FAILED(hr = pMC->Run()))
  828.                 {
  829.                     Msg(TEXT("Failed(0x%08lx) to reset media clip!\r\n"), hr);
  830.                     break;
  831.                 }
  832.             }
  833.         }
  834.     }
  835.  
  836.     return hr;
  837. }
  838.  
  839.  
  840. void CheckSizeMenu(WPARAM wParam)
  841. {
  842.     WPARAM nItems[4] = {ID_FILE_SIZE_HALF,    ID_FILE_SIZE_DOUBLE,
  843.                         ID_FILE_SIZE_NORMAL,  ID_FILE_SIZE_THREEQUARTER};
  844.  
  845.     // Set/clear checkboxes that indicate the size of the video clip
  846.     for (int i=0; i<4; i++)
  847.     {
  848.         // Check the selected item
  849.         CheckMenuItem(ghMenu, (UINT) nItems[i],
  850.                      (UINT) (wParam == nItems[i]) ? MF_CHECKED : MF_UNCHECKED);
  851.     }
  852. }
  853.  
  854.  
  855. void EnablePlaybackMenu(BOOL bEnable)
  856. {
  857.     WPARAM nItems[14] = {ID_FILE_PAUSE,       ID_FILE_STOP,
  858.                         ID_FILE_MUTE,         ID_SINGLE_STEP,
  859.                         ID_FILE_SIZE_HALF,    ID_FILE_SIZE_DOUBLE,
  860.                         ID_FILE_SIZE_NORMAL,  ID_FILE_SIZE_THREEQUARTER,
  861.                         ID_FILE_FULLSCREEN,   ID_RATE_INCREASE,
  862.                         ID_RATE_DECREASE,     ID_RATE_NORMAL,
  863.                         ID_RATE_HALF,         ID_RATE_DOUBLE};
  864.  
  865.     // Set/clear checkboxes that indicate the size of the video clip
  866.     for (int i=0; i<14; i++)
  867.     {
  868.         // Check the selected item
  869.         EnableMenuItem(ghMenu, (UINT) nItems[i],
  870.                      (UINT) (bEnable) ? MF_ENABLED : MF_GRAYED);
  871.     }
  872. }
  873.  
  874.  
  875. LRESULT CALLBACK AboutDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  876. {
  877.     switch (message)
  878.     {
  879.         case WM_INITDIALOG:
  880.             return TRUE;
  881.  
  882.         case WM_COMMAND:
  883.             if (wParam == IDOK)
  884.             {
  885.                 EndDialog(hWnd, TRUE);
  886.                 return TRUE;
  887.             }
  888.             break;
  889.     }
  890.     return FALSE;
  891. }
  892.  
  893.  
  894. LRESULT CALLBACK WndMainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  895. {
  896.     switch(message)
  897.     {
  898.         // Resize the video when the window changes
  899.         case WM_MOVE:
  900.         case WM_SIZE:
  901.             if ((hWnd == ghApp) && (!g_bAudioOnly))
  902.                 MoveVideoWindow();
  903.             break;
  904.  
  905.         // Enforce a minimum size
  906.         case WM_GETMINMAXINFO:
  907.             {
  908.                 LPMINMAXINFO lpmm = (LPMINMAXINFO) lParam;
  909.                 lpmm->ptMinTrackSize.x = MINIMUM_VIDEO_WIDTH;
  910.                 lpmm->ptMinTrackSize.y = MINIMUM_VIDEO_HEIGHT;
  911.             }
  912.             break;
  913.  
  914.         case WM_KEYDOWN:
  915.  
  916.             switch(toupper((int) wParam))
  917.             {
  918.                 // Frame stepping
  919.                 case VK_SPACE:
  920.                 case '1':
  921.                     StepOneFrame();
  922.                     break;
  923.  
  924.                 // Frame stepping (multiple frames)
  925.                 case '2':
  926.                 case '3':
  927.                 case '4':
  928.                 case '5':
  929.                 case '6':
  930.                 case '7':
  931.                 case '8':
  932.                 case '9':
  933.                     StepFrames((int) wParam - '0');
  934.                     break;
  935.  
  936.                 case VK_LEFT:       // Reduce playback speed by 25%
  937.                     ModifyRate(-0.25);
  938.                     break;
  939.  
  940.                 case VK_RIGHT:      // Increase playback speed by 25%
  941.                     ModifyRate(0.25);
  942.                     break;
  943.  
  944.                 case VK_DOWN:       // Set playback speed to normal
  945.                     SetRate(1.0);
  946.                     break;
  947.  
  948.                 case 'P':
  949.                     PauseClip();
  950.                     break;
  951.  
  952.                 case 'S':
  953.                     StopClip();
  954.                     break;
  955.  
  956.                 case 'M':
  957.                     ToggleMute();
  958.                     break;
  959.  
  960.                 case 'F':
  961.                 case VK_RETURN:
  962.                     ToggleFullScreen();
  963.                     break;
  964.  
  965.                case 'H':
  966.                     InitVideoWindow(1,2);
  967.                     CheckSizeMenu(wParam);
  968.                     break;
  969.                 case 'N':
  970.                     InitVideoWindow(1,1);
  971.                     CheckSizeMenu(wParam);
  972.                     break;
  973.                 case 'D':
  974.                     InitVideoWindow(2,1);
  975.                     CheckSizeMenu(wParam);
  976.                     break;
  977.                 case 'T':
  978.                     InitVideoWindow(3,4);
  979.                     CheckSizeMenu(wParam);
  980.                     break;
  981.  
  982.                 case VK_ESCAPE:
  983.                     if (g_bFullscreen)
  984.                         ToggleFullScreen();
  985.                     else
  986.                         CloseClip();
  987.                     break;
  988.  
  989.                 case VK_F12:
  990.                 case 'Q':
  991.                 case 'X':
  992.                     CloseClip();
  993.                     break;
  994.             }
  995.             break;
  996.  
  997.         case WM_COMMAND:
  998.  
  999.             switch(wParam)
  1000.             { // Menus
  1001.  
  1002.                 case ID_FILE_OPENCLIP:
  1003.                     // If we have ANY file open, close it and shut down DShow
  1004.                     if (g_psCurrent != Init)
  1005.                         CloseClip();
  1006.  
  1007.                     // Open the new clip
  1008.                     OpenClip();
  1009.                     break;
  1010.  
  1011.                 case ID_FILE_EXIT:
  1012.                     CloseClip();
  1013.                     PostQuitMessage(0);
  1014.                     break;
  1015.  
  1016.                 case ID_FILE_PAUSE:
  1017.                     PauseClip();
  1018.                     break;
  1019.  
  1020.                 case ID_FILE_STOP:
  1021.                     StopClip();
  1022.                     break;
  1023.  
  1024.                 case ID_FILE_CLOSE:
  1025.                     CloseClip();
  1026.                     break;
  1027.  
  1028.                 case ID_FILE_MUTE:
  1029.                     ToggleMute();
  1030.                     break;
  1031.  
  1032.                 case ID_FILE_FULLSCREEN:
  1033.                     ToggleFullScreen();
  1034.                     break;
  1035.  
  1036.                 case ID_HELP_ABOUT:
  1037.                     DialogBox(ghInst, MAKEINTRESOURCE(IDD_ABOUTBOX),
  1038.                               ghApp,  (DLGPROC) AboutDlgProc);
  1039.                     break;
  1040.  
  1041.                 case ID_FILE_SIZE_HALF:
  1042.                     InitVideoWindow(1,2);
  1043.                     CheckSizeMenu(wParam);
  1044.                     break;
  1045.                 case ID_FILE_SIZE_NORMAL:
  1046.                     InitVideoWindow(1,1);
  1047.                     CheckSizeMenu(wParam);
  1048.                     break;
  1049.                 case ID_FILE_SIZE_DOUBLE:
  1050.                     InitVideoWindow(2,1);
  1051.                     CheckSizeMenu(wParam);
  1052.                     break;
  1053.                 case ID_FILE_SIZE_THREEQUARTER:
  1054.                     InitVideoWindow(3,4);
  1055.                     CheckSizeMenu(wParam);
  1056.                     break;
  1057.  
  1058.                 case ID_SINGLE_STEP:
  1059.                     StepOneFrame();
  1060.                     break;
  1061.  
  1062.                 case ID_RATE_DECREASE:     // Reduce playback speed by 25%
  1063.                     ModifyRate(-0.25);
  1064.                     break;
  1065.                 case ID_RATE_INCREASE:     // Increase playback speed by 25%
  1066.                     ModifyRate(0.25);
  1067.                     break;
  1068.                 case ID_RATE_NORMAL:       // Set playback speed to normal
  1069.                     SetRate(1.0);
  1070.                     break;
  1071.                 case ID_RATE_HALF:         // Set playback speed to 1/2 normal
  1072.                     SetRate(0.5);
  1073.                     break;
  1074.                 case ID_RATE_DOUBLE:       // Set playback speed to 2x normal
  1075.                     SetRate(2.0);
  1076.                     break;
  1077.  
  1078.             } // Menus
  1079.             break;
  1080.  
  1081.  
  1082.         case WM_GRAPHNOTIFY:
  1083.             HandleGraphEvent();
  1084.             break;
  1085.  
  1086.         case WM_CLOSE:
  1087.             SendMessage(ghApp, WM_COMMAND, ID_FILE_EXIT, 0);
  1088.             break;
  1089.  
  1090.         case WM_DESTROY:
  1091.             PostQuitMessage(0);
  1092.             break;
  1093.  
  1094.         default:
  1095.             return DefWindowProc(hWnd, message, wParam, lParam);
  1096.  
  1097.     } // Window msgs handling
  1098.  
  1099.     // Pass this message to the video window for notification of system changes
  1100.     if (pVW)
  1101.         pVW->NotifyOwnerMessage((LONG_PTR) hWnd, message, wParam, lParam);
  1102.  
  1103.     return DefWindowProc(hWnd, message, wParam, lParam);
  1104. }
  1105.  
  1106.  
  1107. int PASCAL WinMain(HINSTANCE hInstC, HINSTANCE hInstP, LPSTR lpCmdLine, int nCmdShow)
  1108. {
  1109.     MSG msg={0};
  1110.     WNDCLASS wc;
  1111.     USES_CONVERSION;
  1112.  
  1113.     // Initialize COM
  1114.     if(FAILED(CoInitialize(NULL)))
  1115.     {
  1116.         Msg(TEXT("CoInitialize Failed!\r\n"));
  1117.         exit(1);
  1118.     }
  1119.  
  1120.     // Was a filename specified on the command line?
  1121.     if(lpCmdLine[0] != '\0')
  1122. #ifdef UNICODE
  1123.         MultiByteToWideChar(CP_ACP, 0, lpCmdLine, -1, g_szFileName, MAX_PATH);       
  1124. #else
  1125.         lstrcpy(g_szFileName, lpCmdLine);
  1126. #endif
  1127.  
  1128.     // Set initial media state
  1129.     g_psCurrent = Init;
  1130.  
  1131.     // Register the window class
  1132.     ZeroMemory(&wc, sizeof wc);
  1133.     wc.lpfnWndProc = WndMainProc;
  1134.     ghInst = wc.hInstance = hInstC;
  1135.     wc.lpszClassName = CLASSNAME;
  1136.     wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU);
  1137.     wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  1138.     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  1139.     wc.hIcon         = LoadIcon(hInstC, MAKEINTRESOURCE(IDI_PLAYWND));
  1140.     if(!RegisterClass(&wc))
  1141.     {
  1142.         Msg(TEXT("RegisterClass Failed! Error=0x%x\r\n"), GetLastError());
  1143.         CoUninitialize();
  1144.         exit(1);
  1145.     }
  1146.  
  1147.     // Create the main window.  The WS_CLIPCHILDREN style is required.
  1148.     ghApp = CreateWindow(CLASSNAME, APPLICATIONNAME,
  1149.                     WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN,
  1150.                     CW_USEDEFAULT, CW_USEDEFAULT,
  1151.                     CW_USEDEFAULT, CW_USEDEFAULT,
  1152.                     0, 0, ghInst, 0);
  1153.  
  1154.     if(ghApp)
  1155.     {
  1156.         // Save menu handle for later use
  1157.         ghMenu = GetMenu(ghApp);
  1158.  
  1159.         // Open the specified media file or prompt for a title
  1160.         PostMessage(ghApp, WM_COMMAND, ID_FILE_OPENCLIP, 0);
  1161.  
  1162.         // Main message loop
  1163.         while(GetMessage(&msg,NULL,0,0))
  1164.         {
  1165.             TranslateMessage(&msg);
  1166.             DispatchMessage(&msg);
  1167.         }
  1168.     }
  1169.     else
  1170.     {
  1171.         Msg(TEXT("Failed to create the main window! Error=0x%x\r\n"), GetLastError());
  1172.     }
  1173.  
  1174.     // Finished with COM
  1175.     CoUninitialize();
  1176.  
  1177.     return (int) msg.wParam;
  1178. }
  1179.  
  1180.  
  1181.